#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include <sys/time.h>
#include <rts_socket.h>
#include "rsi_sockif.h"
#include "rvs_msg.h"
#include "rvs_helpif.h"
#include "../rvpncif/rvm_vpnmsg.h"

#define TESTUSERID01	"test01010000001"
#define TESTUSERID02	"test01010000002"
#define TESTUSERID03	"test01010000003"
#define TESTUSERID04	"test01010000004"
#define TESTUSERID05	"test01010000005"
#define TESTUSERID06	"test01010000006"
#define TESTUSERID07	"test01010000007"

static const char* _userids[] = {
    TESTUSERID01,
    TESTUSERID02,
    TESTUSERID03,
    TESTUSERID04,
    TESTUSERID05,
    TESTUSERID06,
    TESTUSERID07,
};

static rvs_status_t _statinfo[] = {
    { RVS_STAT_NONE, 0, NULL },
    { RVS_STAT_NONE, 4, &_userids[1] },
    { RVS_STAT_VPN, 0, NULL },
    { RVS_STAT_REG, 0, NULL },
    { RVS_STAT_CONN, 0, NULL },
    { RVS_STAT_CONN, 3, _userids },
    { RVS_STAT_REG, 2, &_userids[3] },
    { RVS_STAT_VPN, 1, &_userids[5] },
    { RVS_STAT_CONN, 7, _userids },
    { RVS_STAT_CONN, 5, &_userids[1] },
    { RVS_STAT_REG, 6, _userids },
    { RVS_STAT_CONN, 6, &_userids[1] },
    { RVS_STAT_CONN, 1, &_userids[4] },
};

static rvs_error_t _errinfo[] = {
    { VPN_CONN_REQ, RVS_REASON_3, {{ 0x0201, 0x0403 }}, NULL },
    { VPN_DISC_IND, RVS_REASON_2, {{ 0x1211, 0x1413 }}, NULL },
    { VPN_DISC_REQ, RVS_REASON_3, {{ 0x2625, 0x2827 }}, NULL },
    { VPN_STAT_ENQ, RVS_REASON_3, {{ 0x6261, 0x6463 }}, NULL },
    { P2P_CONN_REQ, RVS_REASON_2, {{ 0x3231, 0x3433 }}, TESTUSERID06 },
    { P2P_DISC_IND, RVS_REASON_3, {{ 0x4A49, 0x4C4B }}, TESTUSERID03 },
    { P2P_DISC_REQ, RVS_REASON_2, {{ 0x5655, 0x5857 }}, TESTUSERID02 },
    { P2P_STAT_ENQ, RVS_REASON_3, {{ 0x7675, 0x7877 }}, NULL },
    { ERROR_IND,    RVS_REASON_2, {{ 0x8C8B, 0x8E8D }}, NULL },
    { P2P_CONN_REQ, RVS_REASON_3, {{ 0x917A, 0x0505 }}, TESTUSERID04 },
    { P2P_DISC_REQ, RVS_REASON_3, {{ 0x1234, 0x5678 }}, TESTUSERID07 },
    { 0, 0, {{ 0, 0 }}, 0 },
};

#define NUM_USERIDS		(sizeof(_userids)/sizeof(const char*))
#define NUM_STATINFO	(sizeof(_statinfo)/sizeof(rvs_status_t))
#define	NUM_ERRINFO		(sizeof(_errinfo)/sizeof(rvs_error_t))

rts_callbacks_t rtscbs;

typedef struct _req_param {
    int request;
    int pendno;
    void* context;
    int nparam;
    int args[0];
} req_param_t;

#define MAX_PENDING		10
pthread_t ptid[MAX_PENDING];
int pend[MAX_PENDING];

#ifdef _DO_LOG_
static void logprintf(const char*fmt, ...)
{
    va_list ap;
    struct timeval tv;

    gettimeofday(&tv, NULL);
    fprintf (stderr, "%08d: ", (int)tv.tv_usec);
    va_start(ap, fmt);
    vfprintf(stderr, fmt, ap);
    va_end(ap);

    fflush(stderr);
}

static const char* inet_ntoa6(struct in6_addr addr)
{
    static char buffer[64];
    int i;
    char* cur=buffer;

    for (i=0; i<7; i++) {
        cur += sprintf( cur, "%04x:", addr.s6_addr16[i] );
    };
    sprintf( cur, "%04x", addr.s6_addr16[i] );

    return buffer;
}

static const char* print_addr(struct sockaddr* addr)
{
    static char buffer[128];

    if (addr->sa_family == AF_INET) {
        struct sockaddr_in* sin = (struct sockaddr_in*)addr;
        sprintf ( buffer, "%s:%d", inet_ntoa(sin->sin_addr), ntohs(sin->sin_port));
    } else if (addr->sa_family == AF_INET6) {
        struct sockaddr_in6* sin6 = (struct sockaddr_in6*)addr;
        sprintf ( buffer, "%s[%d]", inet_ntoa6(sin6->sin6_addr), ntohs(sin6->sin6_port));
    } else {
        sprintf ( buffer, "invalid family[%d]", addr->sa_family);
    }

    return buffer;
}
#else
#define logprintf(f,...)	{}
#endif

static void* pending_thread(void* param)
{
    req_param_t* req = (req_param_t*)param;
    int result;
    void* context = req->context;

    switch (req->request) {
    case RAPI_M_CONNECT:
        if (req->nparam == 3) {
	  int err;
            result = connect(req->args[0],
                             (struct sockaddr*)req->args[1],
                             (socklen_t)req->args[2]);
	    err = errno;
            logprintf( "(connect)  ==> %d(%d)\n", result, errno);
            logprintf( "cb_connect(result=%d, context=%p)\n",
                    result, context);
            (*rtscbs.cb_connect)(result, context);
        }
        break;
    case RAPI_M_ACCEPT:
        if (req->nparam == 3) {
            int fd = req->args[0];
            struct sockaddr* addr = (struct sockaddr*)req->args[1];
            socklen_t *addrlen = (socklen_t*)req->args[2];
            result = accept(fd, addr, addrlen);
            logprintf( "(accept)  ==> %d\n", result);
            if (result < 0) {
                logprintf( "cb_accept(result=%d, context=%p)\n",
                        result, context);
            } else {
                logprintf( "cb_accept(addr=%s, result=%d, context=%p)\n",
                        print_addr(addr), result, context);
            }
            (*rtscbs.cb_accept)(addr, addrlen, result, context);
        }
        break;
    case RAPI_M_RECVFROM:
        if (req->nparam == 6) {
            int fd = req->args[0];
            void *buf = (void*)req->args[1];
            int len = req->args[2];
            int flags = req->args[3];
            struct sockaddr *from = (struct sockaddr*)req->args[4];
            socklen_t *fromlen = (socklen_t*)req->args[5];

            result = recvfrom(fd, buf, len, flags, from, fromlen);
            logprintf( "(recvfrom)  ==> %d\n", result);
            if (result >= 0) {
                logprintf( "cb_recvfrom(buf=%p, from=%s, result=%d, context=%p)\n",
                        buf, print_addr(from), result, context);
            } else {
                logprintf( "cb_recvfrom(result=%d, context=%p)\n",
                        result, context);
            }
            (*rtscbs.cb_recvfrom)(buf, from, fromlen, result, context);
        }
        break;
    case RAPI_M_RECV:
        if (req->nparam == 4) {
            int fd = req->args[0];
            void *buf = (void*)req->args[1];
            int count = req->args[2];
            int flags = req->args[3];

            result = recv(fd, buf, count, flags);
            logprintf( "(recv)  ==> %d\n", result);
            logprintf( "cb_recv(buf=%p, result=%d, context=%p)\n",
                        buf, result, context);
            (*rtscbs.cb_recv)(buf, result, context);
        }
        break;
    case RAPI_M_SELECT:
        if (req->nparam == 5) {
            int nd = req->args[0];
            fd_set *rfds = (fd_set *)req->args[1];
            fd_set *wfds = (fd_set *)req->args[2];
            fd_set *efds = (fd_set *)req->args[3];
            struct timeval *tv = (struct timeval *)req->args[4];

            result = select(nd, rfds, wfds, efds, tv);
            logprintf( "(select)  ==> %d\n", result);
            logprintf( "cb_select(readfds=%p, writefds=%p, exceptfds=%p, context=%p)\n",
                    rfds, wfds, efds, context);
            (*rtscbs.cb_select)(rfds, wfds, efds, result, context);
        }
        break;
    default:
        break;
    }

    pend[req->pendno] = 0;
    free(req);
    return NULL;
}

static pthread_t delay_request(int request, void* context, int nparam, ...)
{
    va_list ap;
    int i, find=MAX_PENDING;
    req_param_t* param = malloc(sizeof(req_param_t)+nparam*sizeof(int));

    param->request = request;
    param->context = context;
    param->nparam = nparam;

    va_start(ap, nparam);
    for (i=0; i<nparam; i++) {
        param->args[i] = va_arg(ap, int);
    }
    va_end(ap);

    while (find == MAX_PENDING) {
        for (find=0; find<MAX_PENDING; find++) {
            if (ptid[find] == 0) break;
            if (!pend[find]) {
                pthread_join(ptid[find], NULL);
                ptid[find] = 0;
                break;
            }
        }
        if (find == MAX_PENDING) usleep(10000);
    } 

    param->pendno = find;
    pend[find] = 1;

    if (pthread_create(&ptid[find], NULL, pending_thread, param) < 0) {
        free (param);
        return (pthread_t)(-1);
    }

    return ptid[find];
}

int rts_init(char *lvip, char *rvip, int level)
{
    memset(&rtscbs, 0, sizeof(rtscbs));
    return 0;
}

void rts_set_callbacks(rts_callbacks_t *cbs)
{
    memcpy (&rtscbs, cbs, sizeof(rtscbs));
}

int rts_socket(int domain, int type, int protocol, void *context)
{
    int sock;
    logprintf( "rts_socket(domain=%d, type=%d, protocol=%d, context=%p)\n",
             domain, type, protocol, context);

    sock = socket(domain, type, protocol);

    logprintf( "(socket)  ==> %d\n", sock);

    logprintf( "cb_socket(result=%d, context=%p)\n",
            sock, context);

    (*rtscbs.cb_socket)(sock, context);

    return sock;
}

int rts_close(int fd, void *context)
{
    int result;
    logprintf( "rts_close(fd=%d, context=%p)\n",
             fd, context);

    result = close(fd);

    logprintf( "(close)  ==> %d\n", result);

    logprintf( "cb_close(result=%d, context=%p)\n",
            result, context);

    (*rtscbs.cb_close)(result, context);

    return result;
}

int rts_bind(int fd, struct sockaddr *addr, socklen_t addrlen, void *context)
{
    int result;
    int err;
    logprintf( "rts_bind(fd=%d, addr=%s, context=%p)\n",
             fd, print_addr(addr), context);

    result = bind(fd, addr, addrlen);
    err = errno;
    logprintf( "(bind)  ==> %d(%d)\n", result, err);

    logprintf( "cb_bind(result=%d, context=%p)\n",
            result, context);

    (*rtscbs.cb_bind)(result, context);

    return result;
}

int rts_connect(int fd, struct sockaddr *addr, socklen_t addrlen, void *context)
{
    logprintf(
             "rts_connect(fd=%d, addr=%s, context=%p)\n",
             fd, print_addr(addr), context);

    return delay_request (RAPI_M_CONNECT, context, 3, fd, addr, addrlen);
}

int rts_listen(int fd, int backlog, void *context)
{
    int result;
    logprintf(
             "rts_listen(fd=%d, backlog=%d, context=%p)\n",
             fd, backlog, context);

    result = listen(fd, backlog);

    logprintf( "(listen)  ==> %d\n", result);

    logprintf( "cb_listen(result=%d, context=%p)\n",
            result, context);

    (*rtscbs.cb_listen)(result, context);

    return result;
}

int rts_accept(int fd, struct sockaddr *addr, socklen_t *addrlen, void *context)
{
    logprintf(
             "rts_accept(fd=%d, addr=%p, addrlen=%d, context=%p)\n",
             fd, addr, *addrlen, context);

    return delay_request (RAPI_M_ACCEPT, context, 3, fd, addr, addrlen);
}

int rts_send(int fd, const void *buf, size_t count, int flags, void *context)
{
    int result;
    logprintf(
             "rts_send(fd=%d, buf=%p, count=%d, flags=%d, context=%p)\n",
             fd, buf, count, flags, context);

    result = send(fd, buf, count, flags);

    logprintf( "(send)  ==> %d\n", result);

    logprintf( "cb_send(result=%d, context=%p)\n",
                result, context);

    (*rtscbs.cb_send)(result, context);

    return result;
}

int rts_recv(int fd, void *buf, size_t count, int flags, void *context)
{
    logprintf(
             "rts_recv(fd=%d, buf=%p, count=%d, flags=%d, context=%p)\n",
             fd, buf, count, flags, context);

    return delay_request(RAPI_M_RECV, context, 4, fd, buf, count, flags);
}

int rts_sendto(int fd, const void *buf, int len, int flags, struct sockaddr *to, socklen_t tolen, void *context)
{
    int result;
    logprintf(
             "rts_sendto(fd=%d, buf=%p, len=%d, flags=%d, to=%s, context=%p)\n",
             fd, buf, len, flags, print_addr(to), context);

    result = sendto(fd, buf, len, flags, to, tolen);

    logprintf( "(sendto)  ==> %d\n", result);

    logprintf( "cb_sendto(result=%d, context=%p)\n",
                result, context);

    (*rtscbs.cb_sendto)(result, context);

    return result;
}

int rts_recvfrom(int fd, void *buf, int len, int flags, struct sockaddr *from, socklen_t *fromlen, void *context)
{
    logprintf(
             "rts_recvfrom(fd=%d, buf=%p, len=%d, flags=%d, from=%p, fromlen=%d, context=%p)\n",
             fd, buf, len, flags, from, *fromlen, context);

    return delay_request(RAPI_M_RECVFROM, context, 6,
                         fd, buf, len, flags, from, fromlen);
}

int rts_select(int nd, fd_set *rfds, fd_set *wfds, fd_set *efds, struct timeval *tv, void *context)
{
    if (tv) {
        logprintf(
                 "rts_select(nfds=%d, readfds=%p, writefds=%p, exceptfds=%p, timeout=%d:%d, context=%p)\n",
                 nd, rfds, wfds, efds, (int)tv->tv_sec, (int)tv->tv_usec, context);
    } else {
        logprintf(
                 "rts_select(nfds=%d, readfds=%p, writefds=%p, exceptfds=%p, timeout=NULL, context=%p)\n",
                 nd, rfds, wfds, efds, context);
    }

    return delay_request(RAPI_M_SELECT, context, 5, nd, rfds, wfds, efds, tv);
}

int rts_getpeername(int fd, struct sockaddr *name, socklen_t *namelen, void *context)
{
    return -1;
}

int rts_getsockname(int fd, struct sockaddr *name, socklen_t *namelen, void *context)
{
    int result;
#ifdef _DO_LOG_
    struct sockaddr_in* sin = (struct sockaddr_in*)name;
#endif

    logprintf(
             "rts_getsockname(fd=%d, name=%p, namelen=%d, context=%p)\n",
             fd, name, *namelen, context);

    result = getsockname(fd, name, namelen);

    logprintf( "(getsockname)  ==> %d\n", result);

    logprintf( "cb_getsockname(name=%s:%d, context=%p)\n",
            inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), context);

    (*rtscbs.cb_getsockname)(name, namelen, result, context);

    return result;
}

int rts_getsockopt(int fd, int level, int name, void *val, socklen_t *avalsize, void *context)
{
    int result;
    unsigned int ulval = *(unsigned int*)val;

    logprintf(
             "rts_getsockopt(fd=%d, level=%d, name=%d, val=%p, acalsize=%d, context=%p)\n",
             fd, level, name, val, *avalsize, context);

    result = getsockopt(fd, level, name,  val, avalsize);

    logprintf( "(getsockopt)  ==> %d\n", result);

    ulval = *(unsigned int*)val;
    logprintf( "cb_getsockopt(val=0x%08x context=%p)\n",
            ulval, context);

    (*rtscbs.cb_getsockopt)(val, avalsize, result, context);

    return result;
}

int rts_setsockopt(int fd, int level, int name, void *val, socklen_t valsize, void *context)
{
    int result;
    int err;
#ifdef _DO_LOG_
    unsigned int ulval = *(unsigned int*)val;
#endif

    logprintf(
             "rts_setsockopt(fd=%d, level=%d, name=%d, val=0x%08x, context=%p)\n",
             fd, level, name, ulval, context);

    result = setsockopt(fd, level, name,  val, valsize);
    err=errno;
    logprintf( "(setsockopt)  ==> %d(%d)\n", result, err);

    logprintf( "cb_setsockopt(context=%p)\n",
            context);

    (*rtscbs.cb_setsockopt)(result, context);

    return result;
}

int rts_ioctl(int fd, int request, void *argp, void *context)
{
    int result;
    unsigned int ulval = *(unsigned int*)argp;

    logprintf(
             "rts_ioctl(fd=%d, request=%d, arg=0x%08x, context=%p)\n",
             fd, request, ulval, context);

    result = ioctl(fd, request,  argp);

    logprintf( "(ioctl)  ==> %d\n", result);

    ulval = *(unsigned int*)argp;
    logprintf( "cb_ioctl(arg=0x%08x, context=%p)\n",
            ulval, context);

    (*rtscbs.cb_ioctl)(NULL, 0, result, context);

    return result;
}

int rts_shutdown(int fd, int how, void *context)
{
  int result, err;

    logprintf(
             "rts_shutdown(fd=%d, how=%d, context=%p)\n",
             fd, how, context);

    result = shutdown(fd, how);
    err = errno;
    logprintf( "(shutdown)  ==> %d(%d)\n", result, err);

    logprintf( "cb_shutdown(context=%p)\n",
            context);

    (*rtscbs.cb_shutdown)(result, context);

    return result;
}

int rvm_vif_getaddr(int kind, struct sockaddr* addr, socklen_t* addrlen)
{
    return 0;
}

int rvm_get_vif_route(const struct sockaddr* src, const struct sockaddr* dest, int* gwif)
{
  return 0;
}

int rvm_status_check(struct _rvs_status* statusp, struct _rvs_error* errorp)
{
    const char **rusers = statusp->remotename;
    int p2pmax = statusp->p2pcon;
    int no = rand() % NUM_STATINFO;

    memcpy (statusp, &_statinfo[no], sizeof(rvs_status_t));
    if (statusp->p2pcon && p2pmax && rusers) {
        if (statusp->p2pcon > p2pmax) {
            statusp->p2pcon = p2pmax;
        }
        memcpy (rusers, _statinfo[no].remotename, statusp->p2pcon * sizeof(const char*));
        statusp->remotename = rusers;
    }

    no = rand() % NUM_ERRINFO;
    memcpy (errorp, &_errinfo[no], sizeof(rvs_error_t));

    return 0;
}

int main(int args, char** argv)
{
    char buffer[128];

    srand(time(NULL));
    fprintf(stderr, "=== %s Started. ===\n", argv[0]);

    rsi_initialize(NULL);

    fprintf(stderr, "[quit]> ");
    fflush(stderr);
    while (fgets(buffer, 128, stdin)) {
        if (strncmp(buffer, "quit", 4) == 0) {
            break;
        }
        fprintf(stderr, "[quit]> ");
        fflush(stderr);
    }

    rsi_uninitialize();

    return 0;
}



